package com.itzzj.common.aspect;

import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.itzzj.common.anno.OperateLog;
import com.itzzj.common.utils.AddressUtil;
import com.itzzj.common.utils.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Component
@Aspect
@Slf4j
public class ActionLogAspect {

    @Pointcut("@annotation(com.itzzj.common.anno.OperateLog)")
    public void logPointCut(){}

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 执行方法
        Object result = point.proceed();
        // 保存日志
        this.saveActionLog(point);
        return result;
    }

    /**
     * 保存日志
     * @param joinPoint
     */
    private void saveActionLog(ProceedingJoinPoint joinPoint){
        try {
            ServletRequestAttributes sra = getServletRequestAttributes();
            if(sra == null){
                return;
            }
            // 获取 request
            HttpServletRequest request = sra.getRequest();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取执行方法
            Method method = signature.getMethod();
            // 获取方法参数名
            String[] parameterNames = signature.getParameterNames();
            // 参数类型
            Class[] parameterTypes = signature.getParameterTypes();
            // 获取方法参数值
            Object[] args = joinPoint.getArgs();
            OperateLog operateLog = method.getAnnotation(OperateLog.class);
            if(operateLog != null){
                log.info("执行方法：{}",method.getDeclaringClass().getName()+"."+method.getName());
                // 获取客户使用的操作系统，以及浏览器
                UserAgent userAgent = UserAgentUtil.parse(request.getHeader("user-agent"));
                // 浏览器名称
                String browser = userAgent.getBrowser().toString();
                // 浏览器版本
                String version = userAgent.getVersion();
                // 引擎类型
                String engine = userAgent.getEngine().toString();
                // 引擎版本
                String engineVersion = userAgent.getEngineVersion();
                // 操作系统
                String os = userAgent.getOs().toString();
                // 平台类型
                String platForm = userAgent.getPlatform().toString();
                // 请求方式
                String methodType = request.getMethod();
                StringBuffer url = request.getRequestURL();
                String uri = request.getRequestURI();
                log.info("请求方式：{}",methodType);
                log.info("请求地址：{}",url);
                log.info("请求资源：{}",uri);
                log.info("浏览器：{} 浏览器版本：{} 引擎：{} 引擎版本：{} 操作系统：{} 平台类型：{}",
                        browser,version,engine,engineVersion,os,platForm);
                log.info("方法描述：{} 操作类型：{}",operateLog.desc(),operateLog.actionType().getCode());
                log.info("客户端IP：{}", IpUtil.getIpAddr(request));
                // 现获取客户端的真实 IP，在根据 IP 获取详细的地域信息
                String addresses = AddressUtil.getAddresses(IpUtil.getIpAddr(request), "utf-8");
                log.info("IP的地址：{}",addresses);
            }
            for (String parameterName : parameterNames) {
                log.info("参数名：{}",parameterName);
            }
            for (Object arg : args) {
                log.info("参数值：{}",arg);
            }
            for (Class parameterType : parameterTypes) {
                log.info("参数类型：{}",parameterType.toString());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 异常返回通知，用于拦截异常日志信息
     * @param joinPoint 切入点
     * @param e 异常信息
     */
    @AfterThrowing(pointcut = "logPointCut()",throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint,Throwable e){
        try {
            ServletRequestAttributes sra = getServletRequestAttributes();
            if(sra == null){
                return;
            }
            HttpServletRequest request = sra.getRequest();
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            // 获取方法
            Method method = methodSignature.getMethod();
            // 获取请求类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取方法名
            String methodName = method.getName();
            // 获取异常名称
            String exceptionName = e.getClass().getName();
            // 异常信息
            String message = stackTraceToString(exceptionName, e.getMessage(), e.getStackTrace());
            log.info("类名：{}",className);
            log.info("方法名：{}",methodName);
            log.info("异常名称：{}",exceptionName);
            log.info("异常消息：{}",message);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }

    /**
     * 转换异常信息为字符串
     * @param exceptionName 异常名称
     * @param exceptionMessage 异常信息
     * @param elements 堆栈信息
     * @return
     */
    public String stackTraceToString(String exceptionName,String exceptionMessage,StackTraceElement [] elements){
        StringBuffer strBuffer = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strBuffer.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strBuffer.toString();
        return message;
    }


    private ServletRequestAttributes getServletRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
}
















































